#include "net/module/epoll.h"

namespace net {
bool Epoll::Init() {
  CHECK_EQ(ep_fd_, -1);

  ep_fd_ = ::epoll_create1(EPOLL_CLOEXEC);
  if (ep_fd_ == -1) {
    PLOG(WARNING)<< "epoll_create1 error";
    return false;
  }

  struct epoll_event ev = { 0 };
  events_.resize(MaxEventCnt, ev);
  return true;
}

void Epoll::Stop() {
  if (ep_fd_ == -1) {
    return;
  }

  ::close(ep_fd_);
  ep_fd_ = -1;
  Poller::Stop();
}

bool Epoll::Add(Event* ev, int flag) {
  if (ev == NULL || flag < 0) return false;
  CHECK_EQ(fds_.count(ev->fd), 0);

  struct epoll_event event = { 0 };
  event.data.ptr = (void *) ev;
  if (flag & EV_READ) {
    event.events = EPOLLIN;
  }
  if (flag & EV_WRITE) {
    event.events |= EPOLLOUT;
  }
  ev->event = flag;

  fds_.insert(ev->fd);

  int ret = ::epoll_ctl(ep_fd_, EPOLL_CTL_ADD, ev->fd, &event);
  if (ret == -1) {
    LOG(WARNING)<< "epoll_ctl error, fd: " << ev->fd;
    return false;
  }

  return true;
}

void Epoll::Del(Event* ev, int flag) {
  if (ev == NULL || flag <= 0) return;
  CHECK_EQ(fds_.count(ev->fd), 1);

  struct epoll_event event = { 0 };
  event.data.ptr = (void*) ev;
  if (flag & EV_READ) {
    event.events |= EPOLLIN;
  }
  if (flag & EV_WRITE) {
    event.events |= EPOLLOUT;
  }
  ev->event = flag;

  int ret = ::epoll_ctl(ep_fd_, EPOLL_CTL_DEL, ev->fd, &event);
  if (ret == -1) {
    LOG(WARNING)<< "epoll_ctl error, fd: " << ev->fd;
    return;
  }

  fds_.erase(ev->fd);
}

bool Epoll::Mod(Event*ev, int flag) {
  if (ev == NULL || flag <= 0) return false;
  CHECK_EQ(fds_.count(ev->fd), 1);

  struct epoll_event event = { 0 };
  event.data.ptr = (void*) ev;
  if (flag & EV_READ) {
    event.events |= EPOLLIN;
  }
  if (flag & EV_WRITE) {
    event.events |= EPOLLOUT;
  }
  ev->event = flag;

  int ret = ::epoll_ctl(ep_fd_, EPOLL_CTL_MOD, ev->fd, &event);
  if (ret == -1) {
    LOG(WARNING)<< "epoll_ctl error, fd: " << ev->fd;
    return false;
  }

  return true;
}

void Epoll::Loop() {
  exit_ = false;
  while (!exit_) {
    if (fds_.empty()) {
      ::sleep(1);
      continue;
    }

    struct epoll_event ev = { 0 };
    events_.resize(MaxEventCnt, ev);
    int trigger_cnt = ::epoll_wait(ep_fd_, events_.data(), events_.size(),
                                   1000);
    if (trigger_cnt == -1) {
      if (errno != EINTR) {
        PLOG(WARNING)<< "epoll_wait error";
      }
      continue;
    }

    for (int i = 0; i < trigger_cnt; ++i) {
      struct epoll_event* event = &events_[i];
      Event* ev = (Event*) event->data.ptr;

      short flag = 0;
      if (events_[i].events & EPOLLIN) {
        flag |= EV_READ;
      }
      if (events_[i].events & EPOLLOUT) {
        flag |= EV_WRITE;
      }

      CHECK(fds_.find(ev->fd) != fds_.end()) << ev->fd;
      ev->cb(ev->fd, ev->arg, flag);
    }
  }

  stop_cond_.Signal();
}
}
